#include"loader.h"
#include"comm/elf.h"
//加载内核程序

//使用LBA48位模式读取磁盘
static void read_disk(int sector, int sector_count, uint8_t * buf) 
{
    outb(0x1F6, (uint8_t) (0xE0));

	outb(0x1F2, (uint8_t) (sector_count >> 8));
    outb(0x1F3, (uint8_t) (sector >> 24));		// LBA参数的24~31位
    outb(0x1F4, (uint8_t) (0));					// LBA参数的32~39位
    outb(0x1F5, (uint8_t) (0));					// LBA参数的40~47位

    outb(0x1F2, (uint8_t) (sector_count));
	outb(0x1F3, (uint8_t) (sector));			// LBA参数的0~7位
	outb(0x1F4, (uint8_t) (sector >> 8));		// LBA参数的8~15位
	outb(0x1F5, (uint8_t) (sector >> 16));		// LBA参数的16~23位

	outb(0x1F7, (uint8_t) 0x24);

	// 读取数据
	uint16_t *data_buf = (uint16_t*) buf;
	while (sector_count-- > 0) 
    {
		// 每次扇区读之前都要检查，等待数据就绪
		while ((inb(0x1F7) & 0x88) != 0x8) 
        {}

		// 读取并将数据写入到缓存中
		for (int i = 0; i < SECTOR_SIZE / 2; i++) 
        {
			*data_buf++ = inw(0x1F0);
		}
	}
}

//解析ELF文件格式
static uint32_t load_elf(uint8_t* file)
{
	Elf32_Ehdr* elf_hdr= (Elf32_Ehdr*)file;
	 if ((elf_hdr->e_ident[0] != ELF_MAGIC) || (elf_hdr->e_ident[1] != 'E')
        || (elf_hdr->e_ident[2] != 'L') || (elf_hdr->e_ident[3] != 'F')) 
	{
        return 0;
    }

	for(int i=0;i<elf_hdr->e_phnum;i++)
	{
		Elf32_Phdr* phdr=(Elf32_Phdr*)(file+ elf_hdr->e_phoff) + i;  //e_phoff为偏移量
		//检查是否可加载
		if(phdr->p_type != PT_LOAD)
		{
			continue;
		}

		//使用物理地址，此时分页机制还未打开
        uint8_t * src = file + phdr->p_offset;	//源文件地址

        uint8_t * dest = (uint8_t *)phdr->p_paddr;		//目的地址
        for (int j = 0; j < phdr->p_filesz; j++) 
		{
            *dest++ = *src++;							//按地址复制
        }
		//bss区域全0
		//memsz和filesz不同时，后续要填0
		dest= (uint8_t *)phdr->p_paddr + phdr->p_filesz;
		for (int j = 0; j < phdr->p_memsz - phdr->p_filesz; j++) 
		{
			*dest++ = 0;
		}
	}	
	return elf_hdr->e_entry; //返回程序入口地址
}	

static void die(int code)
{
	while(1)
	{

	}
}

#define PDE_P 	  (1<<0)			//表项有效
#define PDE_W     (1<<1)			//可读写
#define	PDE_PS    (1<<7)			//页大小为4MB


#define CR4_PSE   (1<<4)
#define CR0_RG	  (1<<31)


void enter_page_mode()
{
	//页目录	4kb对齐
	static uint32_t page_dir[1024] __attribute__((aligned(4096))) ={
		[0] = PDE_P | PDE_W | PDE_PS | 0 		//只设置前4mb
	};
	//4MB的页大小
	uint32_t cr4= read_cr4();
	write_cr4(cr4 | CR4_PSE);

	//CR3
	write_cr3((uint32_t)page_dir);

	write_cr0(read_cr0() | CR0_RG);

}


void loader_kernel()
{
    //加载内核程序
    read_disk(100,500,(uint8_t *)SYS_KERNEL_LOAD_ADDR);  
    //打开分页机制
	enter_page_mode();
	//跳转至内核程序
	uint32_t kernel_entry = load_elf((uint8_t *)SYS_KERNEL_LOAD_ADDR);
	if(kernel_entry==0)
	{
		die(1);
	}
	((void (*)(boot_info_t*))kernel_entry)(&boot_info);
}